#include "DijkstraTester.h"

#include <sstream>
#include <fstream>
#include <map>
#include <vector>

#include "BinaryHeap.h"
#include "FibonacciHeap.h"
#include "Dijkstra.h"

using namespace std;
using namespace algo;

unsigned long long DijkstraTester::time(std::istream &in, PriorityQueue<DijkstraHeapNode> &Q) {
  int value;
  std::string cmd;

  Dijkstra *dijkstra = 0;

  map<int, DijkstraNode *> nodes;

  double sum = 0;

  int l_comps;
  size_t line = 0;
  while (in >> cmd) {
    line++;
    if (cmd == "n") {
      in >> value;
      DijkstraNode *node = new DijkstraNode();
      nodes[value] = node;
    }
    else if (cmd == "e") {
      in >> value;
      DijkstraNode *node1 = nodes[value];
      in >> value;
      DijkstraNode *node2 = nodes[value];
      in >> value;
      node1->addEdge(node2, value);
    } else if (cmd == "t") {
      int n1, n2;
      in >> n1; in >> n2;
      DijkstraNode *node1 = nodes[n1];

      for (int i = 0; i < 100; i++) {
        vector<DijkstraNode*> list;
        list.reserve(nodes.size());
        for (map<int, DijkstraNode*>::iterator it = nodes.begin(), end = nodes.end();
            it != end; it++) {
          it->second->reset();
          list.push_back(it->second);
        }
        dijkstra = new Dijkstra(&Q, list);
        comps = 0;
        custom_count = 0;
        custom_count1 = 1;
        long start = getTime();
        dijkstra->run(node1);
        sum += max((long)(getTime()) - start, 0l);
        l_comps = comps;
        delete dijkstra;
      }

      in >> value; 
      break;
    }
  }
  cout << nodes.size() << " " << (double)(sum) / 100
                       << " " << custom_count << " ";

  for (map<int, DijkstraNode*>::iterator it = nodes.begin(), end = nodes.end();
      it != end; it++) {
    delete it->second;
  }

  return 0;
}

void DijkstraTester::timeFile(const std::string &file) {
  ifstream fin(file.c_str(), ifstream::in);

  unsigned long long runtime;

  if (fin.good()) {
    BinaryHeap<DijkstraHeapNode> bheap;
    runtime = time(fin, bheap);

    fin.clear();
    fin.seekg(0);

    FibonacciHeap<DijkstraHeapNode> fheap;
    runtime = time(fin, fheap);
    cout << endl;
  }
}

void DijkstraTester::test(const std::string &test, PriorityQueue<DijkstraHeapNode> &Q) {
  istringstream in(test);
  DijkstraTester::test(in, Q);
}

void DijkstraTester::test(std::istream &in, PriorityQueue<DijkstraHeapNode> &Q) {
  int value;
  std::string cmd;

  Dijkstra *dijkstra = 0;

  map<int, DijkstraNode *> nodes;

  size_t line = 0;
  while (in >> cmd) {
    line++;
    if (cmd == "n") {
      in >> value;
      DijkstraNode *node = new DijkstraNode();
      nodes[value] = node;
    }
    else if (cmd == "e") {
      in >> value;
      DijkstraNode *node1 = nodes[value];
      in >> value;
      DijkstraNode *node2 = nodes[value];
      in >> value;
      node1->addEdge(node2, value);
    } else if (cmd == "t") {
      int n1, n2;
      in >> n1; in >> n2;
      DijkstraNode *node1 = nodes[n1];

      vector<DijkstraNode*> list;
      list.reserve(nodes.size());
      for (map<int, DijkstraNode*>::iterator it = nodes.begin(), end = nodes.end();
          it != end; it++) {
        it->second->reset();
        list.push_back(it->second);
      }
      dijkstra = new Dijkstra(&Q, list);
      dijkstra->run(node1);
      
      DijkstraNode *node2 = nodes[n2];
      in >> value;
      int dist = node2->getDistance() == INT32_MAX ?
        -1 : node2->getDistance();
        
      if (dist != value)
        errors.addError(line, Q.getName(), 
            "expected dist from {0} to {1} as {2}, but was {3} - was doing deleteMin")
          << n1 << n2 << value << dist;
      delete dijkstra;
    }
  }

  for (map<int, DijkstraNode*>::iterator it = nodes.begin(), end = nodes.end();
      it != end; it++) {
    delete it->second;
  }
}

void DijkstraTester::testFile(const std::string &file) {
  ifstream fin(file.c_str(), ifstream::in);

  if (fin.good()) {
    errors.setFile(file);
/*
    BinaryHeap<DijkstraHeapNode> bheap;
    test(fin, bheap);

    errors.printResult();

    fin.clear();
    fin.seekg(0);
*/
    FibonacciHeap<DijkstraHeapNode> fheap;
    test(fin, fheap);

    errors.printResult();
  }
}

void DijkstraTester::testFolder(const std::string &dir) {
  vector<string> files = listFolder(dir);

  for (size_t i = 0; i < files.size(); i++) {
    testFile(files[i]);
  }
}

void DijkstraTester::timeFolder(const std::string &dir) {
  vector<string> files = listFolder(dir);

  for (size_t i = 0; i < files.size(); i++) {
    timeFile(files[i]);
  }
}
